package com.yunshipei.core.manager;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.text.TextUtils;

import com.yunshipei.core.BuildConfig;
import com.yunshipei.core.common.ContentValues;
import com.yunshipei.core.common.XCloudException;
import com.yunshipei.core.common.db.DBManager;
import com.yunshipei.core.model.AdapterApplication;
import com.yunshipei.core.model.ConfigInfo;
import com.yunshipei.core.model.XCloudAppModel;
import com.yunshipei.core.model.XCloudRuntimeModel;
import com.yunshipei.core.net.DownloadApi;
import com.yunshipei.core.net.EnterClient;
import com.yunshipei.core.net.ServiceApi;
import com.yunshipei.core.utils.FileUtils;
import com.yunshipei.core.utils.StringUtils;
import com.yunshipei.core.utils.YspLogUtils;

import org.json.JSONException;
import org.json.JSONObject;
import org.reactivestreams.Publisher;

import java.io.File;
import java.io.InputStream;
import java.util.List;

import io.reactivex.Flowable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.FormBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

import static com.yunshipei.core.manager.XCloudDataHandler.KEY_ADAPTER_CFG;
import static com.yunshipei.core.manager.XCloudDataHandler.KEY_RUNTIME_VERSION;
import static com.yunshipei.core.manager.XCloudDataHandler.VALUE_ADAPTER_CFG;
import static com.yunshipei.core.manager.XCloudDataHandler.VALUE_ADAPTER_NAME;
import static com.yunshipei.core.manager.XCloudDataHandler.VALUE_RUNTIME_ENC_MANAGER_SERVER;

/**
 * 下载该manifest的适配包和运行时
 */
public class AdapterPkgHandler {

    private static final String HUB_SERVER = "https://hub.enterplorer.com";
    private static final String HUB_TEST_SERVER = "https://hubtest.enterplorer.com";
    static final String TABLE_ADAPTER_PKG_INFO = "adapter_info";
    static final String TABLE_RUNTIME_ENGINE_INFO = "runtime_engine_info";

    private Context context;
    private String mAppFilePath;
    private XCloudAppModel xCloudAppModel;
    private List<AdapterApplication> adapterApplications;

    public AdapterPkgHandler(Context context, XCloudAppModel xCloudAppModel) {
        this.context = context;
        this.mAppFilePath = context.getFilesDir().getPath();
        this.xCloudAppModel = xCloudAppModel;
        this.adapterApplications = XCloud2Map.getInstance().getConfigInfo().getAdapterApplications();
    }

    public Flowable<XCloudAppModel> start() {
        if (adapterApplications == null || adapterApplications.size() == 0) {
            return Flowable.error(new XCloudException("无可用适配包,请联系管理员..."));
        }
        return Flowable.zip(runtimeDownloadFlowable(), adapterPkgDownloadFlower(), new BiFunction<Boolean, Boolean, XCloudAppModel>() {
            @Override
            public XCloudAppModel apply(Boolean aBoolean, Boolean aBoolean2) throws Exception {
                return xCloudAppModel;
            }
        }).flatMap(new Function<XCloudAppModel, Publisher<XCloudAppModel>>() {
            @Override
            public Publisher<XCloudAppModel> apply(XCloudAppModel xCloudAppModel) throws Exception {
                return Flowable.just(xCloudAppModel);
            }
        });
    }

    public Flowable<XCloudAppModel> starOnlyRuntime() {
        return runtimeDownloadFlowable().flatMap(new Function<Boolean, Publisher<XCloudAppModel>>() {
            @Override
            public Publisher<XCloudAppModel> apply(Boolean aBoolean) throws Exception {
                return Flowable.just(xCloudAppModel);
            }
        });
    }

    private Flowable<Boolean> runtimeDownloadFlowable() {
        final String runtimeVersion = xCloudAppModel.getRuntimeVersion();
        String pVersion = "";
        String rVersion = "";
        boolean isSpecificVersion = true;
        if (runtimeVersion.startsWith("^")) {
            //非特殊版本，即通用版本，1.+，2.+的版本
            rVersion = runtimeVersion.replace("^", "");
            pVersion = rVersion.split("\\.")[0] + ".+";
            isSpecificVersion = false;
        } else {
            pVersion = runtimeVersion.split("\\.")[0] + ".+";
            rVersion = runtimeVersion;
        }
        XCloudRuntimeModel xCloudRuntimeModel = new XCloudRuntimeModel(pVersion, rVersion, isSpecificVersion);
        String cid = XCloud2Map.getInstance().getConfigInfo().getCompanyId();
        RequestBody requestBody = generateRuntimeDownloadRequestBody(cid, xCloudRuntimeModel);
        if (requestBody != null) {
            final String fileName = isSpecificVersion ? runtimeVersion : pVersion;
            final String rootPath = mAppFilePath + File.separator + ContentValues.APP_ENGINE_ROOT_PATH;
            final String tempFileName = rootPath + File.separator + fileName + "_temp";
            ConfigInfo configInfo = XCloud2Map.getInstance().getConfigInfo();
            boolean isManagerRuntime = configInfo.isDownloadRuntimeFromManager();
            final String runtimeDownloadUrl = isManagerRuntime ? configInfo.getUrl() : generateHubServer(configInfo.getCompanyName());
            final String finalRtDownloadUrl = runtimeDownloadUrl + "/api/runtime/download";
            ServiceApi serviceApi = new Retrofit.Builder().baseUrl(runtimeDownloadUrl).client(isManagerRuntime ? EnterClient.getInstances().getManagerClient() : EnterClient.getInstances().getHubClient()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build().create(ServiceApi.class);
            YspLogUtils.rtFileDownloadStart(finalRtDownloadUrl, xCloudAppModel.getScope(), xCloudAppModel.getRuntimeVersion());
            return serviceApi.runtimeDownloadRequest(requestBody).flatMap(new Function<Response<ResponseBody>, Publisher<Boolean>>() {
                @Override
                public Publisher<Boolean> apply(Response<ResponseBody> response) throws Exception {
                    YspLogUtils.d("运行时：" + response.toString());
                    String errorMsg;
                    int code = response.code();
                    if (200 == code) {
                        InputStream inputStream = response.body().byteStream();
                        boolean unZipSuccess = FileUtils.unZipStream(inputStream, tempFileName);
                        if (unZipSuccess) {
                            File oldFile = new File(rootPath + File.separator + fileName);
                            FileUtils.deletePathReal(oldFile.getPath());
                            File newFile = new File(tempFileName);
                            boolean renameSuccess = newFile.renameTo(oldFile);
                            if (renameSuccess) {
                                //存储当前运行时对应的解密Manager服务器地址
                                DBManager dbManager = DBManager.getInstance();
                                SQLiteDatabase db = dbManager.openDB(context);
                                db.beginTransaction();
                                android.content.ContentValues contentValues = new android.content.ContentValues();
                                contentValues.put(KEY_RUNTIME_VERSION, fileName);
                                contentValues.put(VALUE_RUNTIME_ENC_MANAGER_SERVER, XCloud2Map.getInstance().getConfigInfo().getEncManagerServer());
                                Cursor c2 = db.query(TABLE_RUNTIME_ENGINE_INFO, new String[]{VALUE_RUNTIME_ENC_MANAGER_SERVER}, KEY_RUNTIME_VERSION + "=?", new String[]{fileName}, null, null, null);
                                if (c2.moveToNext()) {
                                    db.update(TABLE_RUNTIME_ENGINE_INFO, contentValues, KEY_RUNTIME_VERSION + "=?", new String[]{fileName});
                                } else {
                                    db.insert(TABLE_RUNTIME_ENGINE_INFO, null, contentValues);
                                }
                                c2.close();
                                db.setTransactionSuccessful();
                                db.endTransaction();
                                dbManager.closeDB();
                                return Flowable.just(true);
                            } else {
                                errorMsg = String.format("运行时%s保存失败", fileName);
                            }
                        } else {
                            errorMsg = String.format("运行时%s解压失败", fileName);
                        }
                    } else if (204 == code && runtimeVersion.startsWith("^")) {
                        return Flowable.just(true);
                    } else {
                        errorMsg = String.format("运行时%s请求出错，错误状态码:" + code, fileName);
                    }
                    return Flowable.error(new XCloudException(errorMsg));
                }
            }).doOnError(new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    FileUtils.deletePathReal(tempFileName);
                    YspLogUtils.rtFileDownloadError(finalRtDownloadUrl, throwable.getMessage(), xCloudAppModel.getScope(), xCloudAppModel.getRuntimeVersion());
                }
            }).doOnNext(new Consumer<Boolean>() {
                @Override
                public void accept(Boolean aBoolean) throws Exception {
                    YspLogUtils.rtFileDownloadEnd(finalRtDownloadUrl, xCloudAppModel.getScope(), xCloudAppModel.getRuntimeVersion());
                }
            }).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io());
        }
        return Flowable.just(true);
    }

    /**
     * 运行时下载请求参数处理，不同的参数对应不同的下载方式
     * 逻辑：
     * 1、如果是特殊版本，则判断本地是否有特殊版本，如果有则不去请求，如果没有或者和服务器返回到运行时版本不一致，则重新下载覆盖旧的
     * 2、如果不是特殊版本，则是几+的版本，去本地几加的版本中读取版本号，传给服务器，如果服务器返回204，则本地几+的版本是最新版本，如果返回200，则需要替换几+的版本
     */
    private RequestBody generateRuntimeDownloadRequestBody(String companyId, XCloudRuntimeModel runtimeInfo) {
        final String COMPANY_ID = "companyId";
        final String SOURCE = "source";
        final String PRIMARY_VERSION = "primaryVersion";
        final String CURRENT_VERSION = "currentVersion";
        final String RUNTIME_VERSION = "runtimeVersion";
        final String SPECIFIC_VERSION = "specificVersion";

        FormBody.Builder builder = new FormBody.Builder();
        builder.add(COMPANY_ID, companyId);
        builder.add(SOURCE, "mobile");

        String primaryVersion = runtimeInfo.getPrimaryVersion();
        String runtimeVersion = runtimeInfo.getRuntimeVersion();

        int index = runtimeVersion.indexOf("-");
        if (index != -1) {
            runtimeInfo.setSpecificVersion(true);
        }

        String runtimeRootPath = mAppFilePath + File.separator + ContentValues.APP_ENGINE_ROOT_PATH;
        //是spec版本，文件夹以runtimeVersion的版本命名
        if (runtimeInfo.isSpecificVersion()) {
            //判断本地是不是存在这个版本的运行时
            File pFile = new File(runtimeRootPath, runtimeVersion);
            if (pFile.exists() && pFile.isDirectory() && pFile.length() > 0) {
                String encManagerServer = getRuntimeEncManagerServer(runtimeVersion);
                String encServer = XCloud2Map.getInstance().getConfigInfo().getEncManagerServer();
                if (!TextUtils.isEmpty(encManagerServer) && encManagerServer.equals(encServer)) {
                    return null;
                }
            }
            builder.add(PRIMARY_VERSION, primaryVersion);
            builder.add(SPECIFIC_VERSION, runtimeVersion);
            return builder.build();
        } else {
            //不是special版本，以primaryVersion的版本命名
            File pFile = new File(runtimeRootPath, primaryVersion);
            File[] pFileLists = pFile.listFiles();
            if (!pFile.exists() || pFileLists == null || pFileLists.length == 0) {
                builder.add(PRIMARY_VERSION, primaryVersion);
                return builder.build();
            }

            //有1.+或2.+带+号的运行时，则将本地存在的version拼接在primary版本后面下载
            for (File prFile : pFileLists) {
                if (prFile.isDirectory()) continue;
                if (prFile.getName().equals("package.json")) {
                    String packageStr = FileUtils.readFile(prFile.getAbsolutePath());
                    try {
                        JSONObject jsonObject = new JSONObject(packageStr);
                        String version = jsonObject.optString("version");
                        builder.add(PRIMARY_VERSION, primaryVersion);
                        if (!TextUtils.isEmpty(version)) {
                            //此处是为了兼容旧版运行时与新版运行时过渡问题
                            for (File tempFile : pFileLists) {
                                if (tempFile.getName().endsWith(".enc")) {
                                    String encServer = getRuntimeEncManagerServer(primaryVersion);
                                    String encManagerServer = XCloud2Map.getInstance().getConfigInfo().getEncManagerServer();
                                    if (!TextUtils.isEmpty(encServer) && encServer.equals(encManagerServer)) {
                                        builder.add(RUNTIME_VERSION, runtimeVersion);
                                        builder.add(CURRENT_VERSION, version);
                                        return builder.build();
                                    }
                                }
                            }
                        }
                        return builder.build();
                    } catch (JSONException e) {
                        return builder.add(PRIMARY_VERSION, primaryVersion).build();
                    }
                }
            }
            return builder.add(PRIMARY_VERSION, primaryVersion).build();
        }
    }

    private String getRuntimeEncManagerServer(String runtimeVersion) {
        //查询一下该版本运行时对应的加密字段managerServer是不是一样，不一样也需要重新下载
        DBManager dbManager = DBManager.getInstance();
        SQLiteDatabase db = dbManager.openDB(context);
        Cursor cursor = db.query(TABLE_RUNTIME_ENGINE_INFO, new String[]{VALUE_RUNTIME_ENC_MANAGER_SERVER}, KEY_RUNTIME_VERSION + "=?", new String[]{runtimeVersion}, null, null, null);
        String encServer = "";
        while (cursor.moveToNext()) {
            encServer = cursor.getString(0);
        }
        cursor.close();
        dbManager.closeDB();
        return encServer;
    }

    //生成hub地址，如果使用公网的hub，则可以手动配置hub地址，为测试留的后门
    private String generateHubServer(String companyName) {
        if (BuildConfig.DEBUG) {
            File file = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + "_ysp_hub_server.txt");
            if (file.exists()) {
                String server = FileUtils.readFile(file.getPath());
                if (!TextUtils.isEmpty(server)) {
                    return server.trim();
                }
            }
        }
        return TextUtils.isEmpty(companyName) ? HUB_SERVER : ((companyName.startsWith("ysptest-") || companyName.startsWith("www.ysptest-")) ? HUB_TEST_SERVER : HUB_SERVER);
    }

    private Flowable<Boolean> adapterPkgDownloadFlower() {
        ConfigInfo configInfo = XCloud2Map.getInstance().getConfigInfo();
        String scope = xCloudAppModel.getScope();
        AdapterApplication downloadApp = null;
        for (AdapterApplication adapterApplication : configInfo.getAdapterApplications()) {
            String url = adapterApplication.getDownloadUrl();
            String fileName = StringUtils.getZipPkgDownloadUrlFileName(url);
            if (!TextUtils.isEmpty(fileName) && fileName.equals(scope)) {
                downloadApp = adapterApplication;
                break;
            }
        }
        if (downloadApp == null) {
            return Flowable.error(new XCloudException("没有此适配包,请联系管理员..."));
        }
        final String appNewId = downloadApp.getAppId();

        String eAppId = "";
        String eAppVersion = "";
        DBManager dbManager = DBManager.getInstance();
        SQLiteDatabase db = dbManager.openDB(context);
        Cursor cursor = db.query(TABLE_ADAPTER_PKG_INFO, new String[]{VALUE_ADAPTER_CFG}, KEY_ADAPTER_CFG + "=?", new String[]{appNewId}, null, null, null);
        if (cursor.moveToNext()) {
            eAppId = appNewId;
            eAppVersion = cursor.getString(0);
        }
        cursor.close();
        dbManager.closeDB();
        //判断是否要下载该适配包，条件：本地没有该适配包或者该适配包本地存在，但是版本不同
        final String downloadUrl = downloadApp.getDownloadUrl();
        final String downloadAppVersion = downloadApp.getVersion();
        final String fileName = StringUtils.getZipPkgDownloadUrlFileName(downloadUrl);
        File oldFile = new File(mAppFilePath + File.separator + ContentValues.APP_WEB_ROOT_PATH + File.separator + ContentValues.YSP_NEW_ADAPTER_PAC_PATH + File.separator + fileName);
        if (TextUtils.isEmpty(eAppId) || !eAppVersion.equals(downloadAppVersion) || !oldFile.exists()) {
            YspLogUtils.adapterFileDownloadStart(downloadUrl, xCloudAppModel.getScope(), xCloudAppModel.getRuntimeVersion());
            final String rootPath = mAppFilePath + File.separator + ContentValues.APP_WEB_ROOT_PATH + File.separator + ContentValues.YSP_NEW_ADAPTER_PAC_PATH;
            final String tempFileName = rootPath + File.separator + fileName + "_temp";
            String managerServer = configInfo.getUrl();
            DownloadApi downloadApi = new Retrofit.Builder().client(EnterClient.getInstances().getManagerClient()).baseUrl(managerServer).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build().create(DownloadApi.class);
            return downloadApi.get(downloadUrl).flatMap(new Function<Response<ResponseBody>, Publisher<Boolean>>() {
                @Override
                public Publisher<Boolean> apply(Response<ResponseBody> response) throws Exception {
                    YspLogUtils.d("适配包：" + response.toString());
                    String errorMsg;
                    int code = response.code();
                    if (200 == code) {
                        InputStream inputStream = response.body().byteStream();
                        boolean unZipSuccess = FileUtils.unZipStream(inputStream, tempFileName);
                        if (unZipSuccess) {
                            File oldFile = new File(rootPath + File.separator + fileName);
                            FileUtils.deletePathReal(oldFile.getPath());
                            File newFile = new File(tempFileName);
                            boolean renameSuccess = newFile.renameTo(oldFile);
                            if (renameSuccess) {
                                //更新适配包的版本配置信息
                                android.content.ContentValues contentValues = new android.content.ContentValues();
                                contentValues.put(KEY_ADAPTER_CFG, appNewId);
                                contentValues.put(VALUE_ADAPTER_NAME, fileName);
                                contentValues.put(VALUE_ADAPTER_CFG, downloadAppVersion);
                                DBManager dbManager1 = DBManager.getInstance();
                                SQLiteDatabase db1 = dbManager1.openDB(context);
                                Cursor c2 = db1.query(TABLE_ADAPTER_PKG_INFO, null, KEY_ADAPTER_CFG + "=?", new String[]{appNewId}, null, null, null);
                                if (c2.moveToNext()) {
                                    db1.update(TABLE_ADAPTER_PKG_INFO, contentValues, KEY_ADAPTER_CFG + "=?", new String[]{appNewId});
                                } else {
                                    db1.insert(TABLE_ADAPTER_PKG_INFO, null, contentValues);
                                }
                                c2.close();
                                dbManager1.closeDB();
                                return Flowable.just(true);
                            } else {
                                errorMsg = String.format("适配包%s保存失败", fileName);
                            }
                        } else {
                            errorMsg = String.format("适配包%s解压失败", fileName);
                        }
                    } else {
                        errorMsg = String.format("适配包%s下载请求出错，状态码：" + code, fileName);
                    }
                    return Flowable.error(new XCloudException(errorMsg));
                }
            }).doOnError(new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    FileUtils.deletePathReal(tempFileName);
                    YspLogUtils.adapterFileDownloadError(downloadUrl, throwable.getMessage(), xCloudAppModel.getScope(), xCloudAppModel.getRuntimeVersion());
                }
            }).doOnNext(new Consumer<Boolean>() {
                @Override
                public void accept(Boolean aBoolean) throws Exception {
                    YspLogUtils.adapterFileDownloadEnd(downloadUrl, xCloudAppModel.getScope(), xCloudAppModel.getRuntimeVersion());
                }
            }).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io());
        }
        return Flowable.just(true);
    }
}
